package com.itwxz.forum.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.itwxz.common.constant.UserConstants;
import com.itwxz.common.exception.user.UserNotExistsException;
import com.itwxz.common.exception.user.UserPasswordNotMatchException;
import com.itwxz.common.utils.ServletUtils;
import com.itwxz.common.utils.StringUtils;
import com.itwxz.common.utils.ip.AddressUtils;
import com.itwxz.common.utils.ip.IpUtils;
import com.itwxz.forum.domain.dto.LoginForumDto;
import com.itwxz.forum.domain.po.ForumUser;
import com.itwxz.forum.domain.vo.ForumUserInfoVo;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

@Slf4j
@Component
public class ForumLoginService {
    @Autowired
    private ForumUserService userService;

    public SaResult login(LoginForumDto user) {
        // 登录前置校验
        loginPreCheck(user.getEmail(), user.getPassword());

        ForumUser loginUser = userService.selectUserByUserName(user.getEmail());

        if (loginUser == null) return SaResult.error("用户不存在");

        // 密码校验（数据库存的是加密后的密码）
        if (!BCrypt.checkpw(user.getPassword(), loginUser.getPassword())) {
            return SaResult.error("密码错误");
        }

        loginUser.setPassword(null);
        ForumUserInfoVo userInfo = buildLoginUserInfo(loginUser);

        // 登录成功，使用 Sa-Token 登录
        StpUtil.login(loginUser.getUserId(),
                new SaLoginParameter()
                        .setTerminalExtra("nickName", userInfo.getUserNickName())
                        .setTerminalExtra("ipaddr", userInfo.getIpaddr())
                        .setTerminalExtra("loginLocation", userInfo.getLoginLocation())
                        .setTerminalExtra("browser", userInfo.getBrowser())
                        .setTerminalExtra("os", userInfo.getOs())
        );

        userInfo.setToken(StpUtil.getTokenValue());

        StpUtil.getSession().set("forum-user", userInfo);

        recordLoginInfo(userInfo);

        return SaResult.data(loginUser).set("satoken", userInfo.getToken());
    }

    /**
     * 登录前置校验
     *
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password) {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            throw new UserPasswordNotMatchException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userInfo 用户
     */
    private void recordLoginInfo(ForumUserInfoVo userInfo) {
        ForumUser user = new ForumUser();
        user.setUserId(userInfo.getUserId());
        user.setLoginIp(userInfo.getIpaddr());
        user.setLocation(userInfo.getLoginLocation());

        user.setLoginDate(Instant.ofEpochMilli(userInfo.getLoginTime())
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime());

        userService.updateById(user);
    }

    private ForumUserInfoVo buildLoginUserInfo(ForumUser user) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr();

        return ForumUserInfoVo.builder()
                .user(user)
                .ipaddr(ip)
                .loginLocation(AddressUtils.getRealAddressByIP(ip))
                .browser(userAgent.getBrowser().getName())
                .os(userAgent.getOperatingSystem().getName())
                .loginTime(System.currentTimeMillis())
                .build();
    }
}
